﻿using System;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
#if NETSTANDARD2_1
using exceptionless = Exceptionless.Extensions.Logging;
#endif

namespace CNative.Logging
{
    public static class LoggingHelper
    {
        /// <summary>
        /// Represents a type used to perform logging.
        /// </summary>
        public static ILogger Logger { get; set; } //= new Log4Net.Log4NetLogger();

        public static bool IsTraceEnabled { get { return Logger != null ? Logger.IsEnabled(LogLevel.Trace) : false; } }
        public static bool IsWarnEnabled { get { return Logger != null ? Logger.IsEnabled(LogLevel.Warning) : false; } }
        public static bool IsInfoEnabled { get { return Logger != null ? Logger.IsEnabled(LogLevel.Information) : false; } }
        public static bool IsDebugEnabled { get { return Logger != null ? Logger.IsEnabled(LogLevel.Debug) : false; } }
        public static bool IsErrorEnabled { get { return Logger != null ? Logger.IsEnabled(LogLevel.Error) : false; } }
        public static bool IsCriticalEnabled { get { return Logger != null ? Logger.IsEnabled(LogLevel.Critical) : false; } }

        public static bool UseDispatcher { get; set; } = true;
        private static Dispatchs.SingleThreadDispatcher<Action> mDispatcher;

        public static string LoggerType => AppConfigurtaion.GetAppSettings("Logging:LoggerType", "Nlog");

        static LoggingHelper()
        {
            try
            {
#if NETSTANDARD2_1
                LoggerFactory loggerFactory = new LoggerFactory();
                if (LoggingHelper.LoggerType.Trim().ToLower() == "Log4Net".ToLower())
                {
                    loggerFactory.AddLog4Net();
                }
                else if (LoggingHelper.LoggerType.Trim().ToLower() == "Exceptionless".ToLower())
                {
                    loggerFactory.AddExceptionlessLogger();
                }
                else if (LoggingHelper.LoggerType.Trim().ToLower() == "Exceptionless.Nlog".ToLower())
                {
                    loggerFactory.AddExceptionlessNLogLogger();
                    NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = AppConfigurtaion.Configuration;
                }
                else if (LoggingHelper.LoggerType.Trim().ToLower() == "Console".ToLower())
                {
                    loggerFactory.AddConsoleLogger();
                }
                else if (LoggingHelper.LoggerType.Trim().ToLower() == "Serilog".ToLower())
                {
                    loggerFactory.AddSerilogLogger();
                }
                else
                {
                    loggerFactory.AddNlogFileLogger();
                    NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = AppConfigurtaion.Configuration;
                }
                LoggingHelper.Logger = loggerFactory.CreateLogger("LoggingHelper");

                //var typelog = typeof(Log4Net.Log4NetLogger);
                //var svcServiceCollection = new ServiceCollection();
                //if (LoggingHelper.LoggerType.Trim().ToLower() == "Log4Net".ToLower())
                //{
                //    svcServiceCollection.AddLogging(f => f.AddLog4Net());
                //}
                //else if (LoggingHelper.LoggerType.Trim().ToLower() == "Exceptionless".ToLower())
                //{
                //    svcServiceCollection.AddLogging(f => f.AddExceptionlessLogger());
                //    //typelog = typeof(exceptionless.ExceptionlessLogger);
                //}
                //else if (LoggingHelper.LoggerType.Trim().ToLower() == "Exceptionless.Nlog".ToLower())
                //{
                //    svcServiceCollection.AddLogging(f => f.AddExceptionlessNLogLogger());                   
                //    // typelog = typeof(exceptionless.ExceptionlessLogger);

                //    NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = AppConfigurtaion.Configuration;
                //}
                //else if (LoggingHelper.LoggerType.Trim().ToLower() == "Console".ToLower())
                //{
                //    svcServiceCollection.AddLogging(f => f.AddConsoleLogger());
                //}
                //else if (LoggingHelper.LoggerType.Trim().ToLower() == "Serilog".ToLower())
                //{
                //    svcServiceCollection.AddLogging(f => f.AddSerilogLogger());
                //}
                //else
                //{
                //    svcServiceCollection.AddLogging(f => f.AddNlogFileLogger());
                //    //typelog = typeof(Nlog.FileLogger);

                //    NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = AppConfigurtaion.Configuration;
                //}
                //var svcProvider = svcServiceCollection.BuildServiceProvider();

                //LoggingHelper.Logger = svcProvider.GetRequiredService<ILoggerFactory>().CreateLogger("LoggingHelper");//typelog

                //LoggerFactory loggerFactory = new LoggerFactory();
                //loggerFactory.CreateLogger("Log4Net.Log4NetLogger");
#else
                LoggerFactory loggerFactory = new LoggerFactory(); 
                //var typelog = typeof(Log4Net.Log4NetLogger);
                if (LoggingHelper.LoggerType.Trim().ToLower() == "Log4Net".ToLower())
                {
                    loggerFactory.AddLog4Net();
                } 
                else if (LoggingHelper.LoggerType.Trim().ToLower() == "Console".ToLower())
                {
                   loggerFactory.AddConsoleLogger();
                } 
                else if (LoggingHelper.LoggerType.Trim().ToLower() == "Serilog".ToLower())
                {
                   loggerFactory.AddSerilogLogger();
                }
                else
                {
                    loggerFactory.AddNlogFileLogger();
                    //typelog = typeof(Nlog.FileLogger);

                    NLog.Extensions.Logging.ConfigSettingLayoutRenderer.DefaultConfiguration = AppConfigurtaion.Configuration;
                }
                LoggingHelper.Logger =loggerFactory.CreateLogger("LoggingHelper");// loggerFactory.CreateLogger(typelog);
#endif
                mDispatcher = new Dispatchs.SingleThreadDispatcher<Action>(OnWriteLog);
                UseDispatcher = true;

            }
            catch (Exception e)
            {
                throw new Exception("加载日志配置文件失败", e);
            }
        }

        public static bool IsEnabled(LogLevel logLevel)
        {
            return Logger?.IsEnabled(logLevel) == true;
        }

        public static void AddAction(Action e)
        {
            if (UseDispatcher)
                mDispatcher.Enqueue(e);
        }

        private static void OnWriteLog(Action process)
        {
            try
            {
                process.Invoke();
            }
            catch (Exception e_)
            {
                try
                {
                    Logger?.LogWarning("OnWriteLog Exception:{0}", e_?.ToString());
                }
                catch { }
            }
        }
        /// <summary>
        /// Debug
        /// </summary>
        /// <param name="message"></param>
        public static void Debug(object message)
        {
            if (IsDebugEnabled)
            {
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogDebug(message?.ToString()));
                }
                else
#if NETSTANDARD2_1
                    Logger.LogDebug(message?.ToString());
#else
                    Logger.LogDebug(new EventId(), message?.ToString());
#endif
            }

        }
        public static void Debug(this Exception exception, object message)
        { Debug(message, exception); }
        public static void Debug(this object message, Exception exception)
        {
            if (IsDebugEnabled)
            {
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogDebug(message?.ToString()));
                }
                else
#if NETSTANDARD2_1
                    Logger.LogDebug(exception, message?.ToString());
#else
                    Logger.LogDebug(new EventId(), exception, message?.ToString());
#endif
            }
        }

        /// <summary>
        /// Info
        /// </summary>
        /// <param name="message"></param>
        public static void Info(this object message)
        {
            if (IsInfoEnabled)
            {
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogInformation(message?.ToString()));
                }
                else
#if NETSTANDARD2_1
                    Logger.LogInformation(message?.ToString());
#else
                    Logger.LogInformation(new EventId(), message?.ToString());
#endif
            }
        }
        public static void Info(this Exception exception, object message)
        { Info(message, exception); }
        public static void Info(this object message, Exception exception)
        {
            if (IsInfoEnabled)
            {

#if NETSTANDARD2_1
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogInformation(exception, message?.ToString()));
                }
                else
                    Logger.LogInformation(exception, message?.ToString());
#else
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogInformation(new EventId(),exception, message?.ToString()));
                }
                else
                    Logger.LogInformation(new EventId(), exception, message?.ToString());
#endif
            }
        }

        /// <summary>
        /// Warn
        /// </summary>
        /// <param name="message"></param>
        public static void Warn(this object message)
        {
            if (IsWarnEnabled)
            {
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogWarning(message?.ToString()));
                }
                else
#if NETSTANDARD2_1
                    Logger.LogWarning(message?.ToString());
#else
                    Logger.LogWarning(new EventId(), message?.ToString());
#endif
            }
        }
        public static void Warn(this Exception exception, object message)
        { Warn(message, exception); }
        public static void Warn(this object message, Exception exception)
        {
            if (IsWarnEnabled)
            {
#if NETSTANDARD2_1
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogWarning(exception, message?.ToString()));
                }
                else
                    Logger.LogWarning(exception, message?.ToString());
#else
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogWarning(new EventId(),exception, message?.ToString()));
                }
                else
                    Logger.LogWarning(new EventId(), exception, message?.ToString());
#endif
            }
        }

        /// <summary>
        /// Error
        /// </summary>
        /// <param name="message"></param>
        public static void Error(this object message)
        {
            if (IsErrorEnabled)
            {
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogError(message?.ToString()));
                }
                else
#if NETSTANDARD2_1
                    Logger.LogError(message?.ToString());
#else
                    Logger.LogError(new EventId(), message?.ToString());
#endif
            }
        }
        public static void Error(this Exception exception, object message)
        { Error(message, exception); }
        public static void Error(this object message, Exception exception)
        {
            if (IsErrorEnabled)
            {
#if NETSTANDARD2_1
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogError(exception, message?.ToString()));
                }
                else
                    Logger.LogError(exception, message?.ToString());
#else
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogError(new EventId(),exception, message?.ToString()));
                }
                else
                    Logger.LogError(new EventId(), exception, message?.ToString());
#endif
            }
        }

        /// <summary>
        /// Trace
        /// </summary>
        /// <param name="message"></param>
        public static void Trace(this object message)
        {
            if (IsTraceEnabled)
            {
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogTrace(message?.ToString()));
                }
                else
#if NETSTANDARD2_1
                    Logger.LogTrace(message?.ToString());
#else
                    Logger.LogTrace(new EventId(), message?.ToString());
#endif
            }
        }
        public static void Trace(this Exception exception, object message)
        { Trace(message, exception); }
        public static void Trace(this object message, Exception exception)
        {
            if (IsTraceEnabled)
            {
#if NETSTANDARD2_1
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogTrace(exception, message?.ToString()));
                }
                else
                    Logger.LogTrace(exception, message?.ToString());
#else
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogTrace(new EventId(),exception, message?.ToString()));
                }
                else
                    Logger.LogTrace(new EventId(), exception, message?.ToString());
#endif
            }
        }
        /// <summary>
        /// /Critical
        /// </summary>
        /// <param name="message"></param>
        public static void Critical(this object message)
        {
            if (IsCriticalEnabled)
            {
#if NETSTANDARD2_1
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogCritical(message?.ToString()));
                }
                else
                    Logger.LogCritical(message?.ToString());
#else
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogCritical(new EventId(),message?.ToString()));
                }
                else
                    Logger.LogCritical(new EventId(), message?.ToString());
#endif
            }
        }
        public static void Critical(this Exception exception, object message)
        { Critical(message, exception); }
        public static void Critical(this object message, Exception exception)
        {
            if (IsCriticalEnabled)
            {
#if NETSTANDARD2_1
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogCritical(exception, message?.ToString()));
                }
                else
                    Logger.LogCritical(exception, message?.ToString());
#else
                if (UseDispatcher)
                {
                    AddAction(() => Logger.LogCritical(new EventId(),exception, message?.ToString()));
                }
                else
                    Logger.LogCritical(new EventId(), exception, message?.ToString());
#endif
            }
        }

        /// <summary>
        /// 实现接口ILogger
        /// </summary>
        /// <typeparam name="TState"></typeparam>
        /// <param name="logLevel"></param>
        /// <param name="eventId"></param>
        /// <param name="state"></param>
        /// <param name="exception"></param>
        /// <param name="formatter"></param>
        public static void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
        {
            if (!Logger.IsEnabled(logLevel))
            {
                return;
            }
            if (UseDispatcher)
            {
                AddAction(() => Logger.Log(logLevel, eventId, state, exception, formatter));
            }
            else
#if NETSTANDARD2_1
                Logger.Log(logLevel, eventId, state, exception, formatter);
#else
                Logger.Log(logLevel, eventId, state, exception, formatter);
#endif
        }

    }
}
